package dancrawler.server;

import dancrawler.server.events.DeathEvent;

/**
 * Represents a living creature.
 */
public class Creature extends GameObject {

    private int hp_cur = 10, hp_max = 10;
    private int str = 2, per = 4, def = 1;
    protected GameHandler gh;
    private boolean dead = false;
    protected int moveCD = 300;
    protected int attackCD = 100;
    private long lastAttack = 0;
    private long lastMove = 0;
    private int attackRange = 1;
    private int level = 1;
    private int xp = 0;
    private int xbase = 1000;
    private double xmult = 1.5;
    private int cs = 0;

    public Creature(String type, GameHandler gh, Gameworld gw) {
        super(type, true, gw);
        this.gh = gh;
    }

    /**
     *
     * @return The level of the creature
     */
    public int getLevel() {
        return level;
    }

    public void setLevel(int lvl) {
        level = lvl;
    }

    /**
     * Gives experience, raising the creatures level if the required amount has
     * been collected
     *
     * @param xp The experience to give
     */
    public void giveXp(int xp) {
        this.xp += xp;
        if (this.xp >= xbase * ((Math.pow(xmult, level)) - 1)) {
            level++;
            this.xp = (int) (this.xp - xbase * ((Math.pow(xmult, level)) - 1));
            hp_max+=10;
            str+=1;
            def+=1;
            hp_cur = hp_max;
            
            cs++;

        }
    }
    public int getUStats()
    {
        return cs;
    }
    public void decUStats()
    {
        cs--;
    }

    public int getStrength() {
        return str;
    }

    public void setStrength(int str) {
        this.str = str;
    }

    public void modStrength(int str) {
        this.str += str;
    }

    public int getPerception() {
        return per;
    }

    public void setPerception(int per) {
        this.per = per;
    }

    public void modPerception(int per) {
        this.per += per;
    }

    public int getDef() {
        return def;
    }

    public void setDef(int def) {
        this.def = def;
    }

    /**
     *
     * @param dmg The damage to be taken. It is not adjusted for defense or
     * level.
     * @return true if the damage kills the creature
     */
    public boolean takeDamage(int dmg) {
        if (!dead) {
            this.hp_cur -= dmg;
            if (hp_cur <= 0) {
                this.kill();
            }
            return dead;
        }
        return false;
    }

    public void kill() {
        if (!dead) {
            this.hp_cur = 0;
            this.dead = true;
            gh.getEventDirector().postEvent(new DeathEvent(this));
        }
    }

    public int getMaxHp() {
        return this.hp_max;
    }

    public void setMaxHp(int val) {
        hp_max = val;
        if (hp_cur < hp_max) {
            hp_cur = hp_max;
        }
    }

    public int getCurrentHp() {
        return this.hp_cur;
    }

    public boolean attack(Creature cr) {
        if (dead) {
            return false;
        }
        int dist = Math.max(Math.abs(this.getX() - cr.getX()), Math.abs(this.getY() - cr.getY()));
        if (dist > attackRange) {
            return false;
        }
        if (System.currentTimeMillis() < lastAttack + attackCD) {
            return false;
        }

        lastAttack = System.currentTimeMillis();
        if (cr.takeDamage((int) (1+str * 0.5 * cr.getLevel() / cr.getDef()))) {
            this.giveXp((int) (100 * Math.pow(1.3, cr.getLevel())));
            hp_cur += hp_max * 0.25;
            if (hp_cur < hp_max) {
                hp_cur = hp_max;
            }

        }
        return true;
    }

    public boolean isDead() {
        return dead;
    }

    public boolean move(int direction) {
        if (dead) {
            return false;
        }
        if (System.currentTimeMillis() < lastMove + moveCD) {
            return false;
        }
        lastMove = System.currentTimeMillis();
        return this.getWorld().move(this, direction);
    }
}
